

import os
import re
import hashlib
import asyncio
from typing import Dict, List, Optional, Tuple
from pathlib import Path
import logging

# 文档处理库
try:
    import PyPDF2

    PDF_AVAILABLE = True
except ImportError:
    PDF_AVAILABLE = False
    print("⚠️ PyPDF2 未安装，无法处理PDF文件")

try:
    from docx import Document as DocxDocument

    DOCX_AVAILABLE = True
except ImportError:
    DOCX_AVAILABLE = False
    print("⚠️ python-docx 未安装，无法处理DOCX文件")

try:
    import mammoth

    MAMMOTH_AVAILABLE = True
except ImportError:
    MAMMOTH_AVAILABLE = False

logger = logging.getLogger(__name__)


class DocumentProcessor:
    """增强的文档处理器"""

    def __init__(self, max_file_size: int = 50 * 1024 * 1024):  # 50MB
        self.max_file_size = max_file_size
        self.supported_formats = {
            '.pdf': self._process_pdf,
            '.txt': self._process_txt,
            '.md': self._process_markdown,
            '.docx': self._process_docx,
        }

        # 如果有mammoth，也支持doc格式
        if MAMMOTH_AVAILABLE:
            self.supported_formats['.doc'] = self._process_doc

    async def process_document(self, file_path: str, filename: str = None) -> Dict:
        """
        异步处理文档并返回结构化信息

        Args:
            file_path: 文件路径
            filename: 原始文件名（可选）

        Returns:
            包含文档内容和元数据的字典
        """
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")

        # 验证文件
        is_valid, error_msg = await self.validate_file(file_path)
        if not is_valid:
            raise ValueError(error_msg)

        file_extension = Path(file_path).suffix.lower()

        if file_extension not in self.supported_formats:
            raise ValueError(f"不支持的文件格式: {file_extension}")

        try:
            # 获取文件基本信息
            file_info = await self._get_file_info(file_path, filename)

            # 处理文档内容
            processor = self.supported_formats[file_extension]
            content = await processor(file_path)

            # 清理和预处理文本
            cleaned_content = self._clean_text(content)

            # 生成文档摘要
            summary = self._generate_summary(cleaned_content)

            # 提取关键词
            keywords = self._extract_keywords(cleaned_content)

            # 检测文档类型
            doc_type = self._detect_document_type(cleaned_content, filename)

            # 计算文档统计
            stats = self._calculate_stats(cleaned_content)

            result = {
                "file_info": file_info,
                "content": cleaned_content,
                "summary": summary,
                "keywords": keywords,
                "document_type": doc_type,
                "statistics": stats,
                "processing_status": "success"
            }

            logger.info(f"✅ 文档处理完成: {filename}, 字符数: {len(cleaned_content)}")
            return result

        except Exception as e:
            logger.error(f"❌ 文档处理失败: {file_path}, 错误: {e}")
            raise Exception(f"文档处理失败: {str(e)}")

    async def _get_file_info(self, file_path: str, original_filename: str = None) -> Dict:
        """异步获取文件基本信息"""

        def get_file_stat():
            file_stat = os.stat(file_path)
            with open(file_path, 'rb') as f:
                file_hash = hashlib.md5(f.read()).hexdigest()
            return file_stat, file_hash

        file_stat, file_hash = await asyncio.get_event_loop().run_in_executor(
            None, get_file_stat
        )

        return {
            "original_filename": original_filename or os.path.basename(file_path),
            "file_path": file_path,
            "file_size": file_stat.st_size,
            "file_hash": file_hash,
            "file_extension": Path(file_path).suffix.lower(),
            "processing_time": file_stat.st_mtime
        }

    async def _process_pdf(self, file_path: str) -> str:
        """异步处理PDF文件"""
        if not PDF_AVAILABLE:
            raise Exception("PyPDF2未安装，无法处理PDF文件")

        def extract_pdf_text():
            text_content = []
            try:
                with open(file_path, 'rb') as file:
                    pdf_reader = PyPDF2.PdfReader(file)

                    # 逐页提取文本
                    for page_num, page in enumerate(pdf_reader.pages):
                        try:
                            page_text = page.extract_text()
                            if page_text.strip():
                                text_content.append(f"=== 第{page_num + 1}页 ===\n{page_text}")
                        except Exception as e:
                            logger.warning(f"PDF第{page_num + 1}页处理失败: {e}")
                            continue

            except Exception as e:
                raise Exception(f"PDF处理失败: {e}")

            if not text_content:
                raise Exception("PDF文件无法提取文本内容")

            return "\n\n".join(text_content)

        return await asyncio.get_event_loop().run_in_executor(None, extract_pdf_text)

    async def _process_txt(self, file_path: str) -> str:
        """异步处理文本文件"""

        def read_text_file():
            encodings = ['utf-8', 'gbk', 'gb2312', 'latin-1']

            for encoding in encodings:
                try:
                    with open(file_path, 'r', encoding=encoding) as file:
                        content = file.read()
                        if content.strip():
                            return content
                except UnicodeDecodeError:
                    continue

            raise Exception("无法以支持的编码读取文本文件")

        return await asyncio.get_event_loop().run_in_executor(None, read_text_file)

    async def _process_markdown(self, file_path: str) -> str:
        """异步处理Markdown文件"""
        content = await self._process_txt(file_path)

        # 简单的Markdown处理：移除一些标记符号但保留结构
        def clean_markdown():
            # 保留标题结构，但移除标记
            content_processed = re.sub(r'^#+\s*', '', content, flags=re.MULTILINE)
            # 移除粗体和斜体标记
            content_processed = re.sub(r'\*\*(.*?)\*\*', r'\1', content_processed)
            content_processed = re.sub(r'\*(.*?)\*', r'\1', content_processed)
            # 移除代码标记
            content_processed = re.sub(r'`(.*?)`', r'\1', content_processed)
            # 处理链接
            content_processed = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', content_processed)

            return content_processed

        return await asyncio.get_event_loop().run_in_executor(None, clean_markdown)

    async def _process_docx(self, file_path: str) -> str:
        """异步处理DOCX文件"""
        if not DOCX_AVAILABLE:
            raise Exception("python-docx未安装，无法处理DOCX文件")

        def extract_docx_content():
            try:
                doc = DocxDocument(file_path)
                paragraphs = []

                # 提取段落
                for paragraph in doc.paragraphs:
                    text = paragraph.text.strip()
                    if text:
                        paragraphs.append(text)

                # 提取表格
                for table in doc.tables:
                    for row in table.rows:
                        row_text = []
                        for cell in row.cells:
                            if cell.text.strip():
                                row_text.append(cell.text.strip())
                        if row_text:
                            paragraphs.append(" | ".join(row_text))

                content = "\n\n".join(paragraphs)

                if not content.strip():
                    raise Exception("DOCX文件无内容")

                return content

            except Exception as e:
                raise Exception(f"DOCX处理失败: {e}")

        return await asyncio.get_event_loop().run_in_executor(None, extract_docx_content)

    async def _process_doc(self, file_path: str) -> str:
        """异步处理DOC文件（使用mammoth）"""
        if not MAMMOTH_AVAILABLE:
            raise Exception("mammoth未安装，无法处理DOC文件")

        def extract_doc_content():
            try:
                with open(file_path, "rb") as docx_file:
                    result = mammoth.extract_raw_text(docx_file)
                    return result.value
            except Exception as e:
                raise Exception(f"DOC处理失败: {e}")

        return await asyncio.get_event_loop().run_in_executor(None, extract_doc_content)

    def _clean_text(self, content: str) -> str:
        """清理和预处理文本"""
        if not content:
            return ""

        # 统一换行符
        content = re.sub(r'\r\n|\r', '\n', content)

        # 去除多余空白
        content = re.sub(r'\n\s*\n\s*\n', '\n\n', content)  # 多个空行变成两个
        content = re.sub(r' +', ' ', content)  # 多个空格变成一个

        # 去除特殊字符（保留中文、英文、数字、常用标点）
        content = re.sub(r'[^\u4e00-\u9fff\u0020-\u007f\n]', ' ', content)

        return content.strip()

    def _generate_summary(self, content: str, max_length: int = 200) -> str:
        """生成文档摘要"""
        if len(content) <= max_length:
            return content.strip()

        # 简单的摘要生成：取前几个句子
        sentences = re.split(r'[.!?。！？]', content)
        summary_sentences = []
        current_length = 0

        for sentence in sentences:
            sentence = sentence.strip()
            if not sentence:
                continue

            if current_length + len(sentence) <= max_length:
                summary_sentences.append(sentence)
                current_length += len(sentence)
            else:
                break

        summary = "。".join(summary_sentences)
        if summary and not summary.endswith(('。', '.', '!', '?')):
            summary += "..."

        return summary or content[:max_length] + "..."

    def _extract_keywords(self, content: str, max_keywords: int = 15) -> List[str]:
        """提取关键词"""
        try:
            # 尝试使用jieba
            try:
                import jieba
                import jieba.analyse

                # 使用TF-IDF提取关键词
                keywords = jieba.analyse.extract_tags(content, topK=max_keywords)
                return keywords
            except ImportError:
                pass

            # 备选方案：简单的词频统计
            from collections import Counter
            import re

            # 提取中文词汇和英文单词
            words = re.findall(r'[\u4e00-\u9fff]{2,}|[a-zA-Z]{3,}', content)

            # 过滤停用词
            stop_words = {
                '的', '了', '在', '是', '和', '有', '对', '为', '与', '及', '或', '但', '而', '因为', '所以',
                'the', 'of', 'and', 'a', 'to', 'in', 'is', 'you', 'that', 'it', 'he', 'was', 'for'
            }
            filtered_words = [word for word in words if word not in stop_words and len(word) >= 2]

            # 统计词频
            word_counts = Counter(filtered_words)

            # 返回最常见的关键词
            return [word for word, count in word_counts.most_common(max_keywords)]

        except Exception as e:
            logger.warning(f"关键词提取失败: {e}")
            return []

    def _detect_document_type(self, content: str, filename: str = None) -> str:
        """检测文档类型"""
        content_lower = content.lower()
        filename_lower = filename.lower() if filename else ""

        # 医疗文档类型判断
        medical_keywords = {
            "临床指南": ['指南', 'guideline', '标准', '规范', '共识'],
            "病例报告": ['病例', 'case', '患者', '病人', '诊疗'],
            "药物说明": ['药物', '药品', 'drug', 'medication', '用法', '用量'],
            "研究论文": ['研究', 'study', '实验', '分析', '结果', '结论'],
            "医疗手册": ['手册', 'manual', 'handbook', '操作', '流程'],
            "诊疗规范": ['诊疗', '治疗', '诊断', '症状', '检查']
        }

        # 检查内容和文件名
        for doc_type, keywords in medical_keywords.items():
            if any(keyword in content_lower for keyword in keywords) or \
                    any(keyword in filename_lower for keyword in keywords):
                return doc_type

        return "医疗文档"

    def _calculate_stats(self, content: str) -> Dict[str, int]:
        """计算文档统计信息"""
        return {
            "char_count": len(content),
            "word_count": len(content.split()),
            "paragraph_count": len([p for p in content.split('\n\n') if p.strip()]),
            "line_count": len([line for line in content.split('\n') if line.strip()])
        }

    def get_supported_formats(self) -> List[str]:
        """获取支持的文件格式列表"""
        return list(self.supported_formats.keys())

    async def validate_file(self, file_path: str) -> Tuple[bool, str]:
        """异步验证文件是否可以处理"""
        try:
            if not os.path.exists(file_path):
                return False, "文件不存在"

            def check_file():
                file_size = os.path.getsize(file_path)
                if file_size == 0:
                    return False, "文件为空"

                if file_size > self.max_file_size:
                    return False, f"文件大小超过限制({self.max_file_size / 1024 / 1024:.1f}MB)"

                file_extension = Path(file_path).suffix.lower()
                if file_extension not in self.supported_formats:
                    return False, f"不支持的文件格式: {file_extension}"

                return True, "文件验证通过"

            return await asyncio.get_event_loop().run_in_executor(None, check_file)

        except Exception as e:
            return False, f"文件验证失败: {e}"

    async def batch_process_documents(self, file_paths: List[str]) -> List[Dict]:
        """批量处理文档"""
        tasks = []
        for file_path in file_paths:
            task = self.process_document(file_path)
            tasks.append(task)

        results = await asyncio.gather(*tasks, return_exceptions=True)

        processed_results = []
        for i, result in enumerate(results):
            if isinstance(result, Exception):
                processed_results.append({
                    "file_path": file_paths[i],
                    "processing_status": "failed",
                    "error": str(result)
                })
            else:
                processed_results.append(result)

        return processed_results